<!--
 1.组件只提供了一个简单的虚拟列表的实现，使用了 Vue 3 的 Composition API 和 TypeScript。
 2.此组件应该只提供渲染，不包含任何业务逻辑。
 3.核心逻辑，无论数据有多少条，只渲染可视区域的列表项。
-->
<template>
  <div
    ref="containerRef"
    class="virtual-list-container"
    :style="{
      height: `${containerHeight}px`,
      width: containerWidth + 'px'
    }"
  >
    <!-- 总高度占位元素 -->
    <div
      class="virtual-list-placeholder"
      :style="{ height: `${totalHeight}px` }"
    ></div>

    <!-- 可视区域 -->
    <div
      class="virtual-list-content"
      :style="{ transform: transformY }"
    >
      <!-- 渲染可见项 -->
      <div
        v-for="(item, index) in visibleItems"
        :key="index"
        class="virtual-item"
        :style="{ height: `${itemHeight}px` }"
        @click="emit('itemClick', item)"
      >
        <slot name="item" :item="item" :index="startIndex + index">
          {{ item }}
        </slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'

interface Props {
  items: any[]          // 数据列表
  itemHeight?: number    // 每项高度
  containerHeight?: number // 容器高度
  containerWidth?: number  // 容器宽度
  minRenderedCount?: number // 最小渲染数量（缓冲）
}

const props = withDefaults(defineProps<Props>(), {
  itemHeight: 40,
  containerHeight: 400,
  containerWidth: 300,
  minRenderedCount: 5
})

const emit = defineEmits<{
  (e: 'itemClick', item: any): void
}>()

const containerRef = ref<HTMLElement>()
const scrollTop = ref(0)
const requestRef = ref<number>()

// 计算总高度
const totalHeight = computed(() => props.items.length * props.itemHeight)

// 计算可见项数量
const visibleCount = computed(() =>
  Math.ceil(props.containerHeight / props.itemHeight) + props.minRenderedCount
)

// 计算开始索引
const startIndex = computed(() =>
  Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - props.minRenderedCount)
)

// 计算结束索引
const endIndex = computed(() =>
  Math.min(props.items.length, startIndex.value + visibleCount.value + props.minRenderedCount)
)

// 当前显示的项目
const visibleItems = computed(() =>
  props.items.slice(startIndex.value, endIndex.value)
)

// 转换位置
const transformY = computed(() =>
  `translateY(${startIndex.value * props.itemHeight}px)`
)

// 滚动处理
const handleScroll = () => {
  if (containerRef.value) {
    scrollTop.value = containerRef.value.scrollTop
  }
}

// 优化滚动处理
const smoothScroll = () => {
  if (!requestRef.value) {
    requestRef.value = requestAnimationFrame(() => {
      handleScroll()
      requestRef.value = undefined
    })
  }
}

onMounted(() => {
  if (containerRef.value) {
    containerRef.value.addEventListener('scroll', smoothScroll)
  }
})

onBeforeUnmount(() => {
  if (containerRef.value) {
    containerRef.value.removeEventListener('scroll', smoothScroll)
  }
})
</script>



<style scoped>
.virtual-list-container {
  position: relative;
  overflow-y: auto;
  overflow-x: hidden;
  border: 1px solid #eee;
}

.virtual-list-placeholder {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: -1;
}

.virtual-list-content {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
}

.virtual-item {
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-bottom: 1px solid #eee;
  cursor: pointer;
  transition: background 0.3s;
}

.virtual-item:hover {
  background: #f5f5f5;
}
</style>